﻿/*
 *   Developer : Matt Smith (matt@matt40k.co.uk)
 *   All modified code (c) Matthew Smith all rights reserved
 * 
 *   This modified code is released under Microsoft Reciprocal License (MS-RL).
 *   The license and further copyright text can be found in the file
 *   LICENSE.TXT at the root directory of the distribution.
 *   
 *   Original code is licensed under the Code Project Open License (CPOL) 1.02.
 *   http://www.codeproject.com/info/cpol10.aspx
 *   
 *   Reference: http://www.codeproject.com/Articles/33406/A-C-Tiny-Fireworks-Simulator
 */

/*****************************************************************************/
/* Project  : 
 * */
/* File     : FireWrksBomb.cs                                                */
/* Version  : 1                                                              */
/* Language : C#                                                             */
/* Summary  : The object represents a fireworks bomb with a particle         */
/*            for the launch period and an explosion which delivers          */
/*            many other particles                                           */
/* Creation : 10/01/2009                                                     */
/* Author   : Guillaume CHOUTEAU                                             */
/* History  :                                                                */
/*****************************************************************************/

using System;
using System.Drawing;
using System.Collections.Generic;

namespace Matt40k.Spelling.Fireworks
{
    public class FireworksBomb
    {
        /*****************************************
        *                Fields                  *
        ******************************************/
        #region Fields

        int iNbOfParticules  = 50; // Number of particle generated by the explosion
        int iInitRadialSpeed = 10; // Initial radial speed of an exploded particle
        int iInitLaunchSpeed = 10; // Initial launch speed of the launch paticle
        int iExploseForm     = 0;  // Explosion 

        Color cLauncherColor = Color.Orange; // Color of the launch particle
        Color cParticuleColor = Color.Red;    // Color of the exploded paticles

        FireworksParticle fpLauncherParticule;                                       // Launch particle
        List<FireworksParticle> ltBombParticulesList = new List<FireworksParticle>(); // List of the particles managed by the bomb (Launch + Explosion)
        
        bool bIsBombExplosed = false; // Explosion flag
        
        ForceVector weightForce = new ForceVector(0, (float)(-9.81)); // Force applied on the particles

        #endregion

        /*****************************************
        *              Constructor               *
        ******************************************/
        #region Constructor

        public FireworksBomb(int iMyXPos, Color cMyParticuleColor,int iMyLauchSpeed, int iMyRadialSpeed, int iMyExploseForm, float fMyDeltaTime)
        {
            int iNbLaunchCycles = 0;

            // Number of cycle from launch to apogee for the launch particle
            iNbLaunchCycles = (int)(iMyLauchSpeed / (9.81 * fMyDeltaTime));

            // Init
            cParticuleColor = cMyParticuleColor;
            iInitRadialSpeed = iMyRadialSpeed;
            iInitLaunchSpeed = iMyLauchSpeed;
            iExploseForm = iMyExploseForm;

            // Creation of the launch particle
            PositionVector pvLauncherPosition = new PositionVector(iMyXPos,5);
            SpeedVector svLauncherSpeed = new SpeedVector(0, iInitLaunchSpeed);
            fpLauncherParticule = new FireworksParticle(pvLauncherPosition, svLauncherSpeed, cLauncherColor, 10, iNbLaunchCycles);
            fpLauncherParticule.ApplyForce(weightForce);
            
            // Add the launch particle to the bomb particle list
            ltBombParticulesList.Add(fpLauncherParticule);
        }

        #endregion

        /*****************************************
        *                 Methods                *
        ******************************************/
        #region Methods


        /// <summary>
        /// Update the characteristics of the particles of the bomb particle list
        /// </summary>
        /// <param name="fdeltaTime">Delta time between two updates (seconds)</param>
        /// <returns>The state of the bomb: Not finished = true / Finished  = false</returns>
        public bool Update(float fdeltaTime)
        {
            bool bIsBombAlive = true;

            if (fpLauncherParticule.IsParticleApogee() == false)
            {
                // Only updates the launch particle before its apogee
                fpLauncherParticule.UpdateFireworksParticle(fdeltaTime);
            }
            else
            {
                // Apogee => Explosion
                if (bIsBombExplosed == false)
                {
                    // Add the exploded particle to the bomb particle list
                    ltBombParticulesList = Explose(fpLauncherParticule.GetParticlePosition());
                    bIsBombExplosed = true;
                }

                // Classical update
                foreach (FireworksParticle fpFireParticule in ltBombParticulesList)
                {
                    fpFireParticule.UpdateFireworksParticle(fdeltaTime);
                }

                // Detect the finish of the explosion
                bIsBombAlive = ltBombParticulesList[0].GetFireworksParticuleIsAlive();
            }

            return bIsBombAlive;
        }

        /// <summary>
        /// Return the bomb particle list
        /// </summary>
        /// <returns>The bomb particle list</returns>
        public List<FireworksParticle> GetBombParticleList()
        {
            return ltBombParticulesList;
        }

        /// <summary>
        /// Define a particle list according to the explosion parameters
        /// </summary>
        /// <param name="pvExplosionPosition">The position of the bomb explosion</param>
        /// <returns>A particle list with the exploded particles</returns>
        private List<FireworksParticle> Explose(PositionVector pvExplosionPosition)
        {
            List<FireworksParticle> ltBombParticulesList = new List<FireworksParticle>();

            // Add exploded particle to the list 
            for (int indexSubParticle = 0; indexSubParticle < iNbOfParticules; indexSubParticle++)
            {
                double dParticleSpeedAngle = 2 * Math.PI * indexSubParticle / iNbOfParticules;
                SpeedVector svParticleInitSpeed = new SpeedVector(0, 0);

                // Select initial speed of the particle according to the explosion form
                if (iExploseForm == 2)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * Math.Cos(dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * Math.Sin(dParticleSpeedAngle) * Math.Cos(dParticleSpeedAngle));
                }
                else if (iExploseForm == 3)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * 0.1 * indexSubParticle * Math.Cos(2*dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * 0.1 * indexSubParticle * Math.Sin(2*dParticleSpeedAngle));
                }
                else if (iExploseForm == 4)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * (Math.Cos(dParticleSpeedAngle + Math.PI / 2) + Math.Cos(2 * dParticleSpeedAngle + Math.PI / 2)));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * (Math.Sin(dParticleSpeedAngle + Math.PI / 2) + Math.Sin(2 * dParticleSpeedAngle + Math.PI / 2)));
                }
                else if (iExploseForm == 5)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * (Math.Cos(dParticleSpeedAngle + Math.PI / 2) + Math.Cos(2 * dParticleSpeedAngle + Math.PI / 2)));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * (Math.Sin(dParticleSpeedAngle + Math.PI / 2) + Math.Sin(0.5 * dParticleSpeedAngle + Math.PI / 2)));
                }
                else if (iExploseForm == 6)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * (Math.Cos(0.5*dParticleSpeedAngle)));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * (Math.Sin(0.5*dParticleSpeedAngle)));
                }
                else if (iExploseForm == 7)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * (dParticleSpeedAngle-Math.PI));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * Math.Sin(dParticleSpeedAngle));
                }
                else if (iExploseForm == 8)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * (dParticleSpeedAngle-Math.PI) * (dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * (dParticleSpeedAngle-Math.PI) * (Math.Cos(dParticleSpeedAngle)));
                }
                else if (iExploseForm == 9)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * Math.Sin(2*dParticleSpeedAngle) * Math.Cos(dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * Math.Sin(dParticleSpeedAngle) * Math.Cos(2*dParticleSpeedAngle));
                }
                else if (iExploseForm == 10)
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * Math.Sin(0.2* dParticleSpeedAngle) + Math.Cos(dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * Math.Cos(dParticleSpeedAngle) - Math.Cos(1.5*dParticleSpeedAngle));
                }
                else
                {
                    svParticleInitSpeed.xSpeed = (float)(iInitRadialSpeed * Math.Cos(dParticleSpeedAngle));
                    svParticleInitSpeed.ySpeed = (float)(iInitRadialSpeed * Math.Sin(dParticleSpeedAngle));
                }

                // Create and add the particle
                FireworksParticle fpParticle = new FireworksParticle(pvExplosionPosition, svParticleInitSpeed, cParticuleColor, 20, 50);
                fpParticle.ApplyForce(weightForce);
                ltBombParticulesList.Add(fpParticle);
            }

            // Return the list
            return ltBombParticulesList;
        }

        #endregion
    }
}
